<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>verdaccio下载记录</title>
    <!-- 引入Vue.js -->
    <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
    <!-- 引入Element UI JavaScript 库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!-- 引入Element UI CSS -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" />
    <!-- 引入 ECharts CDN -->
    <script src="https://unpkg.com/echarts@5.4.2/dist/echarts.min.js"></script>
    <script src="https://unpkg.com/moment@2.29.1/min/moment.min.js"></script>
  </head>

  <body>
    <div id="app">
      <el-container>
        <el-header>
          <div class="logoHeader-info">
            <div class="default-logo"></div>
            <div class="default-title">Verdaccio 插件下载量统计</div>
          </div>
        </el-header>
        <div style="padding: 0 20px">
          <el-tabs v-model="activeName" @tab-click="onTabClick">
            <el-tab-pane label="下载总量统计" name="first"></el-tab-pane>
            <!-- <el-tab-pane  disabled label="下载量详情表" name="second"></el-tab-pane> -->
            <el-tab-pane label="周下载量排行榜" name="week"></el-tab-pane>
            <el-tab-pane label="月下载量排行榜" name="month"></el-tab-pane>
          </el-tabs>
        </div>
        <!-- 下载总量统计表 -->
        <el-main v-show="activeName === 'first'">
          <div class="header">
            <div class="header-info"></div>
            <div class="header-btn">
              <el-button type="primary" size="mini" @click="handleClick" :loading="isLogLoading"> 解析实时日志 </el-button>
            </div>
          </div>
          <div class="table-box">
            <el-table :data="tableData" ref="totalRef" size="mini" border stripe :max-height="tableHeight" :default-sort="{prop: 'value', order: 'descending'}">
              <el-table-column prop="packageName" label="插件名" sortable> </el-table-column>
              <el-table-column prop="version" label="latest"> </el-table-column>
              <el-table-column prop="authorName" label="插件作者"> </el-table-column>
              <el-table-column prop="updateNameIp" label="最后更新ip"> </el-table-column>
              <el-table-column prop="totalthisWeekDownloads" label="本周" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column prop="value" label="下载总数" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column label="操作" width="80">
                <template slot-scope="{row}">
                  <el-tooltip content="版本详情" placement="top-start">
                    <el-link :underline="false" @click="onDetailClick(row, 'second')"><i class="el-icon-view el-icon--right"></i> </el-link>
                  </el-tooltip>
                  <el-tooltip content="最近一年周下载量趋势" placement="top-start">
                    <el-link :underline="false" @click="onDetailClick(row,'trend')"><i class="el-icon-data-line el-icon--right"></i> </el-link>
                  </el-tooltip>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-main>
        <!-- 下载量详情表 -->
        <el-main v-show="['trend','second'].includes(activeName)">
          <div class="header">
            <div class="header-info">
              <el-select size="small" v-model="packageName" clearable filterable @change="onPackageNameChange" placeholder="请选择插件名称" style="width: 400px">
                <el-option v-for="item in tableData" :key="item.packageName" :label="item.packageName" :value="item.packageName"> </el-option>
              </el-select>
            </div>
            <div class="header-btn"><el-button type="primary" size="mini" @click="onBackClick">返回</el-button></div>
          </div>
          <div v-if="activeName === 'second'" class="table-box" :key="activeName">
            <el-table ref="detailsRef" :data="detailTableData" size="mini" border stripe :max-height="tableHeight" :default-sort="{prop: 'packageName', order: 'ascending'}">
              <el-table-column prop="packageName" label="插件名" sortable> </el-table-column>
              <!-- <el-table-column prop="version" label="版本号"> </el-table-column> -->
              <el-table-column prop="" label="版本描述"> </el-table-column>
              <el-table-column prop="thisWeekDownloads" label="本周" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column prop="thisMonthDownloads" label="本月" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column prop="weekDownloads" label="最近一周" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column prop="monthDownloads" label="最近一月" sortable width="120" header-align="left" align="right"> </el-table-column>
              <el-table-column prop="value" label="下载总数" sortable width="120" header-align="left" align="right"> </el-table-column>
            </el-table>
          </div>
          <div v-else class="table-box" :key="activeName">
            <div id="chart-container" style="width: 100%; height: 100%"></div>
          </div>
        </el-main>
        <!-- 周榜单 -->
        <el-main v-show="activeName === 'week'">
          <div class="header" style="width: 100%; justify-content: center">
            <div class="header-info">
              <el-radio-group size="mini" v-model="tabWeek">
                <el-radio-button label="thisWeekLogs">本周</el-radio-button>
                <el-radio-button label="lastWeekLogs">最近一周</el-radio-button>
              </el-radio-group>
            </div>
            <div class="header-btn"></div>
          </div>
          <div class="table-box" :key="activeName">
            <el-table :data="tableList[tabWeek]" size="mini" border stripe :max-height="tableHeight" :default-sort="{prop: 'value', order: 'descending'}">
              <el-table-column prop="packageName" label="插件名" align="center"> </el-table-column>
              <el-table-column prop="value" label="下载总数" align="center"> </el-table-column>
            </el-table>
          </div>
        </el-main>
        <!-- 月榜单 -->
        <el-main v-show="activeName === 'month'">
          <div class="header" style="width: 100%; justify-content: center">
            <div class="header-info">
              <el-radio-group size="mini" v-model="tabMonth">
                <el-radio-button label="thisMonthLogs">本月</el-radio-button>
                <el-radio-button label="lastMonthLogs">最近一月</el-radio-button>
              </el-radio-group>
            </div>
            <div class="header-btn"></div>
          </div>
          <div class="table-box" :key="activeName">
            <el-table :data="tableList[tabMonth]" size="mini" border stripe :max-height="tableHeight" :default-sort="{prop: 'value', order: 'descending'}">
              <el-table-column prop="packageName" label="插件名" align="center"> </el-table-column>
              <el-table-column prop="value" label="下载总数" align="center"> </el-table-column>
            </el-table>
          </div>
        </el-main>
      </el-container>
    </div>
  </body>

  <style>
    body {
      padding: 0;
      margin: 0;
      background-color: #f4f4f4;
    }
    #app {
      width: 100%;
      height: 100%;
    }
    .el-container {
      width: 100%;
      height: 100%;
    }
    .logoHeader-info {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
    }
    .default-title {
      margin-left: 20px;
      color: #fff;
      font-weight: 600;
    }
    .el-header {
      height: 64px !important;
      background-color: #4b5e40;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .default-logo {
      display: inline-block;
      vertical-align: middle;
      box-sizing: border-box;
      -webkit-background-position: center;
      background-position: center;
      -webkit-background-size: contain;
      background-size: contain;
      background-image: url();
      background-repeat: no-repeat;
      width: 40px;
      height: 40px;
    }
    .el-main {
      padding: 0 20px 20px 20px;
      display: inline-flex;
      flex-direction: column;
      font-size: 12px !important;
    }
    .header {
      display: flex;
      justify-content: space-between;
      margin-bottom: 20px;
    }
    .header-btn {
    }
    .table-box {
      padding: 0 10px;
      height: calc(100% - 50px);
      overflow: hidden;
    }
    .el-table {
      width: 100%;
      height: 100%;
    }
    .el-table__header {
      font-size: 14px;
    }
    .el-icon--right {
      font-size: 18px;
    }
  </style>

  <script type="module">
    new Vue({
      el: '#app',
      data: {
        tableData: [],
        detailTableData: [],
        activeName: 'first',
        isLogLoading: false,
        packageName: null,
        tableHeight: 0,
        tabWeek: 'thisWeekLogs',
        tabMonth: 'thisMonthLogs',
        tableList: {
          lastMonthLogs: [],
          lastMonthLogs: [],
          thisWeekLogs: [],
          thisMonthLogs: []
        }
      },
      mounted() {
        this.initLocalStorage()

        this.setTableHeight()
        // window.onresize = this.debounce(this.setTableHeight, 200)
      },
      methods: {
        // 防抖
        debounce(func, wait, immediate = false) {
          let timeout

          return function (...args) {
            const context = this

            // 如果设置了立即执行，并且没有定时器，则立即执行
            if (immediate && !timeout) {
              func.apply(context, args)
            }

            // 清除之前的定时器
            clearTimeout(timeout)

            // 设置新的定时器
            timeout = setTimeout(() => {
              timeout = null // 清除定时器
              if (!immediate) {
                func.apply(context, args) // 如果不是立即执行，则在延迟后执行
              }
            }, wait)
          }
        },
        // 切换包名
        onPackageNameChange() {
          this.detailTableData = this.tableData.filter((item) => item.packageName === this.packageName)[0]?.details
        },
        // 版本详情/下载趋势
        onDetailClick(row, type) {
          const { packageName } = row
          this.packageName = packageName
          this.activeName = type
          if (type === 'second') {
            this.onPackageNameChange()
            this.$nextTick(() => {
              this.$refs.detailsRef.doLayout()
            })
          } else {
            this.$nextTick(() => {
              this.getWeeklyDataByPackage()
            })
          }
        },
        getEcharts(data) {
          // 初始化 ECharts 实例
          const chartDom = document.getElementById('chart-container')
          const myChart = echarts.init(chartDom)
          const weeks = this.getWeeksInLastYear()

          const mergeData = () => {
            return weeks.map((i) => {
              const item = data.find((j) => i.weekly === j.weekly)
              return {
                ...i,
                ...item
              }
            })
          }
          console.log(mergeData())
          // 配置图表选项
          const option = {
            title: {
              text: '最近一年周下载量趋势',
              left: 0
            },
            tooltip: {
              trigger: 'axis',
              axisPointer: {
                type: 'line'
              },
              formatter: function (params, ticket, callback) {
                const { axisValue, seriesName, value, data, marker } = params[0]
                const { timeRange } = data

                return `<span>${axisValue}</span><br/><span>时间范围: ${timeRange[0]} to ${timeRange[1]}</span><br/>${marker}<span>${seriesName}: ${value || '--'}</span><br/>`
              }
            },
            grid: [
              {
                left: '3%',
                right: '3%',
                height: '65%'
              },
              {
                left: '3%',
                right: '3%',
                top: '80%',
                height: '10%'
              }
            ],
            xAxis: [
              {
                type: 'category',
                data: weeks.map((i) => i.weekly),
                scale: true,
                boundaryGap: false,
                axisLine: { onZero: false },
                splitLine: { show: false },
                splitNumber: 1,
                axisLabel: {
                  interval: 0 // 强制显示所有标签
                }
              },
              {
                type: 'category',
                gridIndex: 1,
                data: weeks.map((i) => i.weekly),
                scale: true,
                boundaryGap: false,
                axisLine: { onZero: false },
                splitLine: { show: false },
                splitNumber: 1,
                axisLabel: {
                  interval: 0 // 强制显示所有标签
                }
              }
            ],
            yAxis: [
              {
                scale: true,
                splitArea: {
                  show: false
                }
              },
              {
                gridIndex: 1,
                scale: true,
                splitArea: {
                  show: false
                },
                axisLine: { onZero: false },
                axisTick: { show: false },
                splitLine: { show: false },
                axisLabel: { show: true }
              }
            ],
            zoomLock: false,
            dataZoom: [
              {
                type: 'inside',
                xAxisIndex: [0, 0],
                start: 40,
                end: 100
              },
              {
                show: true,
                xAxisIndex: [0, 1],
                type: 'slider',
                bottom: '1%',
                left: '3%',
                right: '3%',
                start: 40,
                end: 100
              }
            ],
            series: [
              {
                name: '下载量',
                type: 'line',
                data: mergeData(),
                smooth: true,
                lineStyle: {
                  normal: {
                    opacity: 0.5
                  }
                }
              },
              {
                name: '下载量',
                type: 'bar',
                barWidth: 10,
                xAxisIndex: 1,
                yAxisIndex: 1,
                data: mergeData()
              }
            ]
          }

          // option.xAxis[0].data = weeks.map((i) => i.weekly)
          // option.xAxis[1].data = weeks.map((i) => i.weekly)
          // option.series[0].data = mergeData()
          // option.series[1].data = mergeData()

          // 使用配置项绘制图表
          myChart.setOption(option)
        },
        getWeeksInLastYear() {
          const weeks = []
          const currentDate = new Date() // 当前日期
          const oneYearAgo = new Date(currentDate)
          oneYearAgo.setFullYear(currentDate.getFullYear() - 1) // 一年前的日期

          let startDate = new Date(oneYearAgo) // 起始日期
          let endDate = new Date(currentDate) // 结束日期

          // 调整起始日期到最近的周一
          while (startDate.getDay() !== 1) {
            startDate.setDate(startDate.getDate() + 1)
          }

          // 生成周数
          while (startDate <= endDate) {
            const weekStart = new Date(startDate)
            const weekEnd = new Date(startDate)
            weekEnd.setDate(weekEnd.getDate() + 6) // 一周的结束日期

            const weekly = moment(weekStart).format('YYYY-w')

            weeks.push({
              weekly: weekly,
              timeRange: [weekStart.toISOString().split('T')[0], weekEnd.toISOString().split('T')[0]]
            })

            startDate.setDate(startDate.getDate() + 7) // 跳到下一周
          }

          return weeks
        },
        // 返回
        onBackClick() {
          this.activeName = 'first'
          this.$nextTick(() => {
            this.$refs.totalRef.doLayout()
          })
        },
        /**
         * 初始化数据
         */
        initLocalStorage() {
          this.getPm2VerdaccioLogs()
          // const data = JSON.parse(window.localStorage.getItem('pm2_downloadLogs'))
          // if (!data?.downloadLogs?.length) {
          //   this.getPm2VerdaccioLogs()
          // } else {
          //   this.filterData(data)
          // }
        },
        /**
         * 过滤数据
         */
        filterData(data) {
          this.isLogLoading = false
          this.tableData = data?.downloadLogs || []
          this.tableList = data?.tableList || {
            lastMonthLogs: [],
            lastMonthLogs: [],
            thisWeekLogs: [],
            thisMonthLogs: []
          }

          window.localStorage.setItem('pm2_downloadLogs', JSON.stringify(data))
        },
        /**
         * 获取日志
         */
        getPm2VerdaccioLogs() {
          this.isLogLoading = true

          fetch(`/api/getPm2VerdaccioLogs`, {
            method: 'get'
          })
            .then((response) => response.json())
            .then((res) => {
              this.$message({
                message: '更新成功',
                type: 'success',
                showClose: true
              })
              this.filterData(res?.data || [])
            })
            .catch(() => this.mockData())
        },
        getWeeklyDataByPackage() {
          fetch(`/api/getWeeklyDataByPackage?f=${this.packageName}`, {
            method: 'get'
          })
            .then((response) => response.json())
            .then((res) => {
              this.$message({
                message: '更新成功',
                type: 'success',
                showClose: true
              })
              this.getEcharts(res?.data || [])
            })
            .catch(() => this.mockData())
        },
        /**
         * 切换tab
         */
        onTabClick() {
          this.$nextTick(() => {
            this.$refs.totalRef.doLayout()
          })
        },
        /**
         * 解析实时日志
         */
        handleClick() {
          this.getPm2VerdaccioLogs()
        },
        /**
         * 设置表格高度
         */
        setTableHeight() {
          this.$nextTick(() => {
            const tb = document.getElementsByClassName('table-box')[0].getBoundingClientRect() || []
            this.tableHeight = tb?.height

            console.log('🚀 ~ :', this.tableHeight)
          })
        },
        /**
         * 模拟数据
         */
        mockData() {
          this.isLogLoading = false
        }
      },
      components: {}
    })
  </script>
</html>
